package JVM.第02篇_类的加载篇;

/**
 * ⑤初始化
 * 初始化阶段，简言之，为类的静态变虽赋子予正确的初始值。
 * 具体描述
 * 类的初始化是类装载的最后一个阶段。
 * 如果前面的步骤都没有问题，那么表示类可以顺利装载到系统中。
 * 此时，类才会开始执行Java字节码。
 * (即:到了初始化阶段，才真正开始执行类中定义的Java 程序代码。)
 *
 * 初始化阶段的重要工作是执行类的初始化方法:<clinit>()方法。
 * ●该方法仅能由Java编译器生成并由JVM调用，程序开发者无法自定义一个同名的方法
 *   更无法直接在Java程序中调用该方法，虽然该方法也是由字节码指令所组成。
 * ●它是由类静态成员的赋值语句以及static语句块合并产生的。
 *
 * <clinit>():只有在给类的中的static的变量显式赋值或在静态代码块中赋值了。
 *              才会生成此方法。
 * <init>():一定会出现在Class的method表中
 *
 * 在加载一个类之前，虚拟机总是会试图加载该类的父类，
 * 因此父类的<clinit>总 是在子类<clinit>之前被调用。
 * 也就是说，父类的static块优先级高于子类。
 * 口诀:由父及子，静态先行。
 *
 * Java编译器并不会为所有的类都产生<clinit>()初始化方法。哪些类在编译为字节码后
 * ，字节码文件中将不会包含<clinit>()方法?
 * ●一个类中并没有声明任何的类变量，也没有静态代码块时
 * ●一个类中声明类变量，但是没有明确使用类变量的初始化语句以及静态代码块来执行
 *   初始化操作时
 * ●一个类中 包含static final修饰的基本数据类型的字段，(引用数据类型会产生)
 *   这些类字段初始化语句采用编译时常量表达式
 *
 *
 * 对于<clinit>()方法的调用，也就是类的初始化，会引发死锁
 * 虚拟机会在内部确保其多线程环境中的安全性。
 * 虚拟机会保证个类的<clinit>()方法在 多线程环境中 被正确地加锁、
 * 同步，如果多个线程同时去初始化一个类，那么只会有一个线程去执行这个类的<clinit>()方法，
 * 其他线程都需要阻塞等待，直到活动线程执行<clinit>()方法完毕。
 * 正是因为函数<clinit>()带锁线程安全的，
 * 因此，如果在一个类的<clinit>()方法中有耗时很长的操作，就可能造成多个线程阻塞，引发死锁。
 * 并且这种死锁是很难发现的，因为看起来它们并没有可用的锁信息。
 * 如果之前的线程成功加载了类，则等在队列中的线程就没有机会再执行<clinit>()方法了。
 * 那么，当需要使用这个类时，虚拟机会直接返回给它己经准备好的信息。
 *
 */
public class A5_类的初始化 extends Thread {

    /**
     * 类的初始化 死锁演示
     */
    public static void main(String[] args) {

        A5_类的初始化 a = new A5_类的初始化();
        a.flag = "A";
        a.start();
        A5_类的初始化 b = new A5_类的初始化();
        b.flag = "B";
        b.start();
    }

    public String flag = "";

    @Override
    public void run() {

        try {
            Class.forName("JVM.第二篇_类的加载篇.Static" + flag);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}
class StaticA{
    static {
        try {
            Thread.sleep(100);
            Class.forName("JVM.第02篇_类的加载篇.StaticB");
            System.out.println("StaticB 加载完成 ");
        } catch (ClassNotFoundException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class StaticB{
    static {
        try {
            Thread.sleep(100);
            Class.forName("JVM.第02篇_类的加载篇.StaticA");
            System.out.println("StaticA 加载完成 ");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
